package com.legsem.jrhosync.ws.rs;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.legsem.jhosync.dto.ClientCreate;
import com.legsem.jhosync.dto.ClientLogin;
import com.legsem.jhosync.face.JhosyncSource;
import com.legsem.jrhosync.samples.CustomerSource;

public class ClientResourceITCase extends AbstractResourceITCase {

    private ClientCreate clientCreate;

    public void setUp() throws Exception {
        super.setUp();
        clientCreate = registerClient("tartempion", "");
    }

    public void testFirstQuery() throws Exception {

        byte[] response = get();

        String readableResponse = new String(response, "UTF-8");
        assertTrue(readableResponse.contains("{\"count\":1}"));
        assertTrue(readableResponse.contains("{\"progress_count\":0}"));
        assertTrue(readableResponse.contains("{\"total_count\":1}"));
        assertTrue(readableResponse.contains("{\"insert\":"));
        assertTrue(readableResponse.contains("\"2\":"));
        assertTrue(readableResponse.contains("\"phone\":\"+33(0)134961000\""));
        assertTrue(readableResponse
                .contains("\"address\":\"Perspective Défense 1 à 5 r du Débarcadère 92700 COLOMBES \""));
        assertTrue(readableResponse.contains("\"name\":\"Aristide Gonfland\""));
        assertTrue(readableResponse.contains("\"company\":\"AREVA\""));
        assertTrue(readableResponse.contains("\"long\":\"2.23419427871704\""));
        assertTrue(readableResponse.contains("\"lat\":\"48.9079465717798\""));

    }

    public void testQueryFollowedByAck() throws Exception {

        byte[] response = get();
        response = ack(response);
        assertEquals(
                "[{\"version\":3},{\"token\":\"\"},{\"count\":0},{\"progress_count\":1},{\"total_count\":1}]",
                new String(response, "UTF-8"));
    }

    public void testCreateOneEntityAndDelete() throws Exception {
        // Start by having client and master document filled (Otherwise, we
        // would get inserts on subsequent queries)
        fillDocuments();

        // create new entity
        create();

        try {
            // next poll request should get the links
            byte[] response = get();

            String readableResponse = new String(response, "UTF-8");
            assertTrue(readableResponse.contains("{\"count\":0}"));
            assertTrue(readableResponse.contains("{\"progress_count\":2}"));
            assertTrue(readableResponse.contains("{\"total_count\":2}"));
            assertTrue(readableResponse
                    .contains("{\"links\":{\"76317685891801\":{\"l\":\"46\"}}}"));

            ack(response);
        } finally {
            delete();
        }

    }

    public void testUpdateOneEntity() throws Exception {
        fillDocuments();

        // create new entity and poll to get everything in sync
        create();
        byte[] response = get();
        ack(response);

        update();

        // wait till the poll interval expires so that the next get is
        // productive
        JhosyncSource syncSource = CustomerSource.class
                .getAnnotation(JhosyncSource.class);
        Thread.sleep(syncSource.poll_interval() * 1000);

        try {
            // next poll request should get the updates
            response = get();

            String readableResponse = new String(response, "UTF-8");
            assertTrue(readableResponse.contains("{\"count\":2}"));
            assertTrue(readableResponse.contains("{\"progress_count\":1}"));
            assertTrue(readableResponse.contains("{\"total_count\":2}"));
            assertTrue(readableResponse
                    .contains("{\"insert\":{\"46\":{\"address\":\"12 rueweyu\",\"lat\":\"75.05\"}}}"));
            assertTrue(readableResponse
                    .contains("{\"delete\":{\"46\":{\"address\":\"45\",\"lat\":\"56\"}}}"));

            ack(response);
        } finally {
            delete();
        }

    }

    public void testDeleteOneEntity() throws Exception {
        fillDocuments();

        // create new entity and poll to get everything in sync
        create();
        byte[] response = get();
        ack(response);

        delete();

        try {
            // next poll request should get the updates
            response = get();

            String readableResponse = new String(response, "UTF-8");
            assertTrue(readableResponse.contains("{\"count\":0}"));
            assertTrue(readableResponse.contains("{\"progress_count\":1}"));
            assertTrue(readableResponse.contains("{\"total_count\":1}"));

            // wait till the poll interval expires so that the next get is
            // productive
            JhosyncSource syncSource = CustomerSource.class
                    .getAnnotation(JhosyncSource.class);
            Thread.sleep(syncSource.poll_interval() * 1000);

            response = get();

            readableResponse = new String(response, "UTF-8");
            assertTrue(readableResponse.contains("{\"count\":0}"));
            assertTrue(readableResponse.contains("{\"progress_count\":1}"));
            assertTrue(readableResponse.contains("{\"total_count\":1}"));

        } finally {
        }

    }

    public void testShouldProduceLoginErrorOnQuery() throws Exception {
        clientCreate = registerClient("", "");
        byte[] response = get();
        String readableResponse = new String(response, "UTF-8");
        assertTrue(readableResponse
                .contains("{\"source-error\":{\"login-error\":{\"message\":\"User name is unknown\"}}}"));
    }

    public void testShouldProduceLoginErrorOnCUD() throws Exception {
        clientCreate = registerClient("", "");
        create();
        byte[] response = get();
        String readableResponse = new String(response, "UTF-8");
        assertTrue(readableResponse
                .contains("{\"source-error\":{\"login-error\":{\"message\":\"User name is unknown\"}}}"));
    }

    public void testShouldProduceQueryErrorOnQuery() throws Exception {
        byte[] response = getJSONRequest("?client_id="
                + clientCreate.getClient().get("client_id")
                + "&p_size=2000&version=3&source_name=Customer&wrongParm=toto");

        String readableResponse = new String(response, "UTF-8");
        assertTrue(readableResponse
                .contains("{\"source-error\":{\"query-error\":{\"message\":\"This source query does not support the wrongParm parameter\"}}}"));
    }

    public void testShouldProduceCreateError() throws Exception {
        fillDocuments();
        postJSONRequest("", "{\"source_name\":\"Customer\","
                + "\"client_id\":\""
                + clientCreate.getClient().get("client_id") + "\","
                + "\"create\":{\"76317685891801\":{" + "\"address\":\"45\","
                + "\"company\":\"adf\"," + "\"lat\":\"56\","
                + "\"long\":\"34\"," + "\"name\":\"qwerty\","
                + "\"phone\":\"23456789\"," + "\"id\":\"exception\"}}}");

        byte[] response = get();
        String readableResponse = new String(response, "UTF-8");
        assertTrue(readableResponse.contains("{\"token\":\"\"}"));
        assertTrue(readableResponse
                .contains("{\"create-error\":{\"76317685891801\":{"));
        assertTrue(readableResponse
                .contains("\"76317685891801-error\":{\"message\":\"BounceException\"}"));
    }

    public void testShouldReturnMetadata() throws Exception {
        byte[] response = getJSONRequest("?client_id="
                + clientCreate.getClient().get("client_id")
                + "&p_size=2000&version=3&source_name=Appointment");

        String readableResponse = new String(response, "UTF-8");
        assertTrue("readableResponse=" + readableResponse,
                readableResponse
                        .contains("{\"metadata\":\"{\\\"indexdata\\\":"));
    }

    /**
     * Register a new client.
     * 
     * @param user the user ID
     * @param password the password
     * @return a client feedback record
     * @throws Exception if something goes wrong
     */
    protected ClientCreate registerClient(String user, String password)
            throws Exception {
        ClientLogin clientLogin = new ClientLogin();
        clientLogin.setLogin(user);
        clientLogin.setPassword(password);
        postJSONRequest("/clientlogin", clientLogin);
        byte[] response = getJSONRequest("/clientcreate");
        return getMapper().readValue(new String(response, "UTF-8"),
                ClientCreate.class);
    }

    /**
     * Issue an initial get followed by an ack to get to a state where the
     * client and master documents reflect the backend.
     */
    protected void fillDocuments() throws Exception {
        byte[] response = get();
        ack(response);

    }

    protected byte[] get() throws Exception {
        return getJSONRequest("?client_id="
                + clientCreate.getClient().get("client_id")
                + "&p_size=2000&version=3&source_name=Customer");
    }

    protected byte[] ack(byte[] response) throws Exception {
        return getJSONRequest("?client_id="
                + clientCreate.getClient().get("client_id")
                + "&p_size=2000&version=3&source_name=Customer" + "&token="
                + getToken(new String(response, "UTF-8")));
    }

    /**
     * request creation of a new entity (the id attribute is not supposed to be
     * here but we use it to mock the generation of a known id that we can
     * check)
     * 
     * @throws Exception
     */
    protected void create() throws Exception {
        postJSONRequest("", "{\"source_name\":\"Customer\","
                + "\"client_id\":\""
                + clientCreate.getClient().get("client_id") + "\","
                + "\"create\":{\"76317685891801\":{" + "\"address\":\"45\","
                + "\"company\":\"adf\"," + "\"lat\":\"56\","
                + "\"long\":\"34\"," + "\"name\":\"qwerty\","
                + "\"phone\":\"23456789\"," + "\"id\":\"46\"}}}");
    }

    protected void update() throws Exception {
        postJSONRequest(
                "",
                "{\"source_name\":\"Customer\","
                        + "\"client_id\":\""
                        + clientCreate.getClient().get("client_id")
                        + "\","
                        + "\"update\":{\"46\":{\"address\":\"12 rueweyu\",\"lat\":\"75.05\"}}}");
    }

    protected void delete() throws Exception {
        postJSONRequest("", "{\"source_name\":\"Customer\","
                + "\"client_id\":\""
                + clientCreate.getClient().get("client_id") + "\","
                + "\"delete\":{\"46\":{" + "\"address\":\"45\","
                + "\"company\":\"adf\"," + "\"lat\":\"56\","
                + "\"long\":\"34\"," + "\"name\":\"qwerty\","
                + "\"phone\":\"23456789\"," + "\"id\":\"46\"}}}");
    }

    protected String getToken(String content) throws IOException {
        Pattern pattern = Pattern.compile("\\{\"token\":\"(\\d+)\"\\}");
        Matcher matcher = pattern.matcher(content);
        matcher.find();
        return matcher.group(1);
    }

}
